package domain

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"cvevulner/util"
)

const (
	BulletinTypeSA       = "SA"
	BulletinTypeBA       = "BA"
	BulletinTypeHotPatch = "HotPatchSA"

	// NoticeTypeCVE is the type for CVE notices
	NoticeTypeCVE = "cve"

	// NoticeTypeBug is the type for bug notices
	NoticeTypeBug = "bug"
)

var (
	regexpBulletin = regexp.MustCompile(`cvrf-openEuler-(\w+)-(\d{4})-(\d{4})\.xml`)
)

type SecurityBulletin struct {
	AffectedVersion []string
	Identification  string
	Date            string
	Component       string
	PatchUrl        []string    // use in hotpatch
	ProductTree     ProductTree // use in coldpatch
	Cves            Cves
}

type ProductTree = map[string][]Product

type Product struct {
	ID       string
	CPE      string
	FullName string
	IsEpol   bool
}

// IsCveNotice returns whether the security bulletin is a CVE notice.
func (s *SecurityBulletin) IsCveNotice() bool {
	return s.GenNoticeType() == NoticeTypeCVE
}

// GenNoticeType returns the type of the security bulletin.
func (s *SecurityBulletin) GenNoticeType() string {
	if strings.Contains(s.Identification, BulletinTypeBA) {
		return NoticeTypeBug
	}

	if strings.Contains(s.Identification, BulletinTypeHotPatch) {
		return NoticeTypeCVE
	}

	if strings.Contains(s.Identification, BulletinTypeSA) {
		return NoticeTypeCVE
	}

	return ""
}

func (s *SecurityBulletin) SetIdentificationOfColdPatch(id int) {
	s.Identification = fmt.Sprintf("openEuler-SA-%d-%d", util.Year(), id)
}

func (s *SecurityBulletin) SetIdentificationOfHotPatch(id int) {
	s.Identification = fmt.Sprintf("openEuler-HotPatchSA-%d-%d", util.Year(), id)
}

func (s *SecurityBulletin) CvrfFileName() string {
	return fmt.Sprintf("cvrf-%s.xml", s.Identification)
}

func (s *SecurityBulletin) PathAppendToIndexFile() string {
	return fmt.Sprintf("%d/%s", util.Year(), s.CvrfFileName())
}

func (s *SecurityBulletin) IsColdPatch() bool {
	return !strings.Contains(s.Identification, BulletinTypeHotPatch)
}

func ParseMaxIdFromIndexTxt(content, bulletinType string) (int, error) {
	split := strings.Fields(content)
	for i := len(split) - 1; i >= 0; i-- {
		v := split[i]

		ret := regexpBulletin.FindStringSubmatch(v)
		if len(ret) != 4 {
			continue
		}

		if ret[1] != bulletinType {
			continue
		}

		year, err := strconv.Atoi(ret[2])
		if err != nil {
			return 0, err
		}

		num, err := strconv.Atoi(ret[3])
		if err != nil {
			return 0, err
		}

		currentYear := util.Year()
		if year != currentYear {
			num = 1000
		}

		return num, nil
	}

	return 0, fmt.Errorf("no match max id")
}
